if
shell 中的 if 语句的作用与其他语言的 if 语句相同: 条件检测.
exit(0) 代表命令运行成功
else 部分就像 then 部分一样, 可以包含任意数量的命令, 包括其他的 if .. then 语句.
if 语句还有另一个特征. 如果 if 后的条件是系列命令, 那么最后一个命令 的 exit 值被用作这个语句块的条件值, 并由此来决定条件是否成立.
if 是如何工作的
- shell 运行 if 之后的命令
- shell 检查命令的 exit 状态
- exit 的状态为 0 意味着成功, 非 0 意味着失败.
- 如果成功, shell 执行 then 部分的代码.
- 如果失败, shell 执行 else 部分的代码
- 关键字 fi 标识 if 块的结束
在 smsh 中增加 if
为了处理 if..then..else..fi 控制流程, 需要在原有的 smsh 增加一层 process, process 通过寻找关键字, 比如 if,then 和 fi, 来管理脚本流程, 在适当的时候调用 fork 和 exec. process 必须记录条件命令的结果以便能够 处理 then 和 else 块.
process 将脚本看做一个接一个的代码区域. 第一个区域就是 then 代码块, 第 2 个区域是 else 代码块, 第三个是在 if 语句之外的代码块.
考虑 if 语句之外的区域, 这里称之为中立区(neutral). 对于这类区域代码, 简单的读一条, 分析一条, 执行一条.
接下载是在 if 和 then 区域. 这个区域中, shell 每执行一条命令就记录下 它的退出状态, 另一区域从 then 到 fi 或 else 之间, 最有一个区域是从 else 到 fi, 在 fi 之后又回到中立区.
shell 记录当前区域类型, 还必须记录在 WANT_THEN 区域中所执行的命令结果.
不同的区域处理方法不同. 特定的区域与程序在特定的状态联系在一起. process 通过 3 个函数来处理区域问题
- is_control_command 返回一个 boolean 变量告诉 process 这条命令是脚本语言的一部分还是一条可执行的命令
- do_control_command 处理关键字 if,then 和 fi. 每个关键字都是区域的界标.这个函数更新状态变量并执行必要的操作
- ok_to_execute 根据当前状态和条件命令的结果返回一个 boolean 值, 说明能搜执行当前命令.
smsh2.c: 修改后的代码
smsh2.c
/** smsh2.c - small - shell version 2 ** small shell that supports command parsing ** and if..then..else..fi logic (by calling process()) **/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include "smsh.h" #define DFL_PROMPT ">" int main() { char *cmdline, *prompt, **arglist; int result, process(char **); void setup(); prompt = DFL_PROMPT; setup(); while ((cmdline = next_cmd(prompt, stdin)) != NULL){ if ((arglist = splitline(cmdline)) != NULL){ result = process(arglist); freelist(arglist); } free(cmdline); } return 0; } void setup() { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } void fatal(char *s1, char *s2, int n) { fprintf(stderr, "Error: %s, %s\n", s1, s2); exit(n); }
process.c
/* process.c * command processing layer * * The process(char **arglist) function is called by the main loop * It sits in front of the execute() function. This layer handles * two main classes of processing: * a) built-in functions (e.g. exit(), set, =, read,..) * b) control structures (e.g. if, while, for) */ #include <stdio.h> #include "smsh.h" int is_control_command(char *); int do_control_command(char **); int ok_to_execute(); int process(char **args) /* * purpose: process user command * returns: result of processing command * details: if a built-in then call appropriate function, if not * execute() * errors: arise form subroutines, handled there */ { int rv = 0; if (args[0] == NULL) rv = 0; else if (is_control_command(args[0])) rv = do_control_command(args); else if (ok_to_execute()) rv = execute(args); return rv; }
/* controlflow.c * * "if" processing is done with two state variables * if_state and if_result */ #include <stdio.h> #include <string.h> #include "smsh.h" enum stats {NEUTRAL, WANT_THEN, THEN_BLOCK}; enum results {SUCCESS, FAIL}; static int if_state = NEUTRAL; static int if_result = SUCCESS; static int last_stat = 0; int syn_err(char *); int ok_to_execute() /* * purpose: datermine the shell should execute a command * returns: 1 for yes, 0 for no * details: if in THEN_BLOCK and if_result was SUCCESS then yes * if in THEN_BLOCK and if_result was FAIL then no * if in WANT_THEN then syntax error (sh is different) */ { int rv = 1; if (if_state == WANT_THEN){ syn_err("then expexted"); rv = 0; }else if (if_state == THEN_BLOCK && if_result == SUCCESS){ rv = 1; }else if (if_state == THEN_BLOCK && if_result == FAIL){ rv = 0; } return rv; } int is_control_command(char *s) /* * purpose: boolean to report if the command is a shell control command * returns: 0 or 1 */ { return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0 || strcmp(s, "fi") == 0); } int do_control_command(char **args) /* * purpose: Process "if", "then", "fi" - change state or detect error * returns: 0 if ok, -1 for syntax error */ { int process(char **); char *cmd = args[0]; int rv = -1; if (strcmp(cmd, "if") == 0){ // 查看代码是否在中立区 if (if_state != NEUTRAL) rv = syn_err("if unexpected"); else{ last_stat = process(args + 1); if_result = (last_stat == 0 ? SUCCESS : FAIL); if_state = WANT_THEN; rv = 0; } }else if(strcmp(cmd, "then") == 0){ if (if_state != WANT_THEN) rv = syn_err("then unexpected"); else{ if_state = THEN_BLOCK; rv = 0; } }else if (strcmp(cmd, "fi") == 0){ if (if_state != THEN_BLOCK) rv = syn_err("fi unexpected"); else{ if_state = NEUTRAL; rv = 0; } }else{ fatal("internal error processing:", cmd, 2); } return rv; } int syn_err(char *msg) /* purpose: handles syntax errors in control structures * details: resets state to NEUTRAL * returns: -1 in interactive mode. Should call fatal in scripts */ { if_state = NEUTRAL; fprintf(stderr, "syntax error: %s\n", msg); return -1; }
编译
cc -o smsh2 smsh2.c splitline.c execute.c process.c controlflow.c